//
//  NetWorkHandler.m
//  NetWorkMgr
//
//  Created by cst_han on 12-8-8.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//

#import "NetWorkHandler.h"
#import "GCDAsyncSocket.h"
#import "AppDelegate.h"
#import "GameHeader.pb.h"
#import "GameUser.pb.h"
#import "GameRoundTripTime.pb.h"
#import "GameACK.pb.h"
#import "GameUser.pb.h"

@implementation NetWorkHandler

@synthesize recvBuf_;
@synthesize sendBuf_;
@synthesize userDefaults_;

#pragma mark - init

-(id)init
{
    self=[super init];
    if(self)
    {
        if(nil==recvBuf_)
        {
            recvBuf_=[NSMutableData alloc];
        }
        
        if(nil==sendBuf_)
        {
            sendBuf_=[NSMutableData alloc];
        }
        
        if(nil==userDefaults_)
        {
            userDefaults_=[NSUserDefaults standardUserDefaults];
        };
    }
    return self;
}
-(BOOL)initConn
{
    if(nil==self)
    {
        NSLog(@"NetWorkHandler is UnInitialized yet");
        return false;
    }

    
    
    //initialize socket
    [self initSocket];
    
    
    //start connect
    if(![self connectToSrv])
    {
        //connect fail
        return NO;
    }
    
    return YES;
    
}


-(void)initSocket
{
    //initialize
    if(nil==asyncSocket_)
    {
        asyncSocket_=[[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
        connSrvAddr_=@"172.22.71.125";
        connSrvPort_=55555;
        
    } 
}

#pragma mark - connect

-(BOOL)connectToSrv
{
    
    if(nil!=asyncSocket_&&[asyncSocket_ isConnected])
    {
        NSLog(@"Socket is already connected");
        return YES;
    }
    
    //if in reconnect state
    if(nil==[asyncSocket_ delegate])
    {
        [asyncSocket_ setDelegate:self];
    }
    
    //check if is connecting to server
    if(isInConnecting_)
        return NO;
    
    
    NSError *connectError=nil;
    //connect
    //Function return YES doesn't mean connect success.
    if(![asyncSocket_ connectToHost:connSrvAddr_ onPort:connSrvPort_ withTimeout:5.0 error:&connectError])
    {
        NSLog(@"Unable to connect to server...");
        NSLog(@"Error code and Description : %@",[connectError localizedDescription]);
        
        UIAlertView *connectAlert=[[UIAlertView alloc]initWithTitle:@"无法连接到服务器" message:nil delegate:self cancelButtonTitle:@"确定" otherButtonTitles: nil];
        
        [connectAlert show];
        
        return NO;
    }
    
    isInConnecting_=YES;
    NSLog(@"Connecting to \"%@\" on port %hu ",connSrvAddr_,connSrvPort_);
    
    return YES;
}




#pragma mark - call back functions

//after connection
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(uint16_t)port
{
    if(isInConnecting_)
    {
        isInConnecting_=NO;
    }
    
    
    if([[AppDelegate sharedAppDelegate] respondsToSelector:@selector(updateNetWorkStatusLabel:)])
    {
        [[AppDelegate sharedAppDelegate]updateNetWorkStatusLabel:@"连接成功"];
    }
    
        
    //begin to read header
    [self readWithSpecificSize:HEADER_SIZE tag:READ_HEADER];
    
}

//after write data
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
    if(tag==WRITE_DATA)
    {
        [sendBuf_ setData:nil];
    }
    
}

//after read data
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    if(nil==data||[data length]<=0)
    {
        NSLog(@"RecvData is empty in didReadData");
        return;
    }
    
    //give to self function to handle
    [self handle_read:data tag:tag];
}


//any socket break up comes to here
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock withError:(NSError *)err
{
    if(isInConnecting_)
    {
        isInConnecting_=NO;
    }
    NSString *errMsg=[err localizedDescription];
    NSLog(@"Socket disConnected with err:%@",errMsg);

    //disconnect clear
    [self disConnect];
    
    
}

#pragma mark - disconnect and check


//check connect state
-(BOOL)isSocketConnected
{
    if([asyncSocket_ isConnected])
        return YES;
    else
        return NO;
}


-(BOOL)disConnect
{
    @try
    {
        isInConnecting_=NO;
        if(nil!=asyncSocket_||[asyncSocket_ isConnected])
        {
            [asyncSocket_ disconnect];
            [asyncSocket_ setDelegate:nil];
        }
        
        NSLog(@"Socket disConnect Completed");
        
        if([[AppDelegate sharedAppDelegate] respondsToSelector:@selector(updateNetWorkStatusLabel:)])
        {
            [[AppDelegate sharedAppDelegate]updateNetWorkStatusLabel:@"无法连接"];
        }
        
        return YES;
    }@catch(NSException *ex)
    {
        isInConnecting_=NO;
        
        NSLog(@"Socket disConnect failed with exception:%@",[ex name]);
        
        return NO;
    }
}

#pragma mark - get set
-(GCDAsyncSocket *)getSocket
{
    return asyncSocket_;
}

-(void)setIsInConnecting:(BOOL)arg_switch
{
    isInConnecting_ =arg_switch;
}

-(BOOL)getIsInConnecting
{
    return isInConnecting_;
}

#pragma mark - send

//write/send
-(BOOL)sendData:(NSData *)arg_sendData Pid:(PID)arg_pid
{
    //check pid is correct one
    if(!PIDIsValidValue(arg_pid))
    {
        NSLog(@"PID into NetWorkHandler:sendData is illegal");
        return NO;
    }
    
    if(nil==arg_sendData||[arg_sendData length]<=0)
    {
        NSLog(@"Arguments into NetWorkHandler:sendData: is nil");
        return NO;
    }
  
    
    if(![self isSocketConnected])
    {
        NSLog(@"Socket is not connected");
        return NO;
    }
    
    if(nil==sendBuf_)
    {
        sendBuf_=[NSMutableData alloc];
    }
    
    Header *_header=[[[[Header builder]setSize:[arg_sendData length]]setPid:arg_pid] build];
    
    [sendBuf_ appendData:[_header data]];
    [sendBuf_ appendData:arg_sendData];
    
    [asyncSocket_ writeData:sendBuf_ withTimeout:-1 tag:WRITE_DATA];
    
    return YES;
}

#pragma mark - TransData Handle


-(BOOL)handle_read:(NSData *)arg_data tag:(long)arg_tag
{
    if(nil==arg_data||[arg_data length]<=0)
    {
        NSLog(@"Data arguments into handle_read is empty");
        return NO;
    }
    
    BOOL _pRslt=false;
    switch (arg_tag) {
        case READ_HEADER:
            //reading header
            _pRslt=[self parseHeader:arg_data];
            
            if(!_pRslt)
            {
                //parse failed
                NSLog(@"parseHeader failed");
                
                [self resetInvali];
                [self disConnect];
                return NO;
            }
            
            //start read content            
            [self readWithSpecificSize:shouldRcvBytes_ tag:READ_BODY];
            
            break;
        case READ_BODY:
        
            recvBytes_+=[arg_data length];
            
            [recvBuf_ appendData:arg_data];
            if(recvBytes_==shouldRcvBytes_)
            {
                //read body ended
                recvBytes_=0;
                shouldRcvBytes_=0;
               
                //start to phaseContent
                _pRslt=[self parseContentByPID:recvBuf_];
                if(!_pRslt)
                {
                    NSLog(@"parseContentByPID failed");
                    
                    [self resetInvali];
                    [self disConnect];
                    
                    return NO;
                }
                
                //again to read header
                [self readWithSpecificSize:HEADER_SIZE tag:READ_HEADER];
                
            }else
            {
                //not ended yet
                
                continueRcvBytes_=shouldRcvBytes_-recvBytes_;
                
                //read the rest
                [self readWithSpecificSize:continueRcvBytes_ tag:READ_BODY];
                
            }
            break;
        default:
        {
            
            NSLog(@"Reading tag is not defined");
            return NO;
        }
            break;
    }
    
    return YES;
    
}
//reading by specific size
-(BOOL)readWithSpecificSize:(int)arg_size tag:(long)arg_tag
{
    if(arg_size<=0)
    {
        NSLog(@"Cannot read with negative size");
        return NO;
    }
    
    if(arg_tag!=READ_BODY&&arg_tag!=READ_HEADER)
    {
        NSLog(@"Tag to read is not defined");
        return NO;
    }
    
    if(![self isSocketConnected])
    {
        NSLog(@"Socket is not connected");
        return NO;
    }
    
    [asyncSocket_ readDataToLength:arg_size withTimeout:-1 tag:arg_tag];
    
    return YES;
}

-(BOOL)parseHeader:(NSData *)arg_data
{
    if(nil==arg_data||[arg_data length]<=0)
    {
        NSLog(@"Data pass into parseHeader is empty");
        return NO;
    }
    
    Header *_header;
    @try {
        _header=[Header parseFromData:arg_data];
    }
    @catch (NSException *exception) {
        NSLog(@"Parsing Header Exception occured:%@,%@",[exception name],[exception reason]);
        
        return NO;
    }
    @finally {
        
        if(nil==_header)
        {
            //parse error
            NSLog(@"Parse Header failed");
            return NO;
        }
    }
    
    //get size and pid
    shouldRcvBytes_=[_header size];
    recvPID_=[_header pid];
    
    return YES;
    
}

-(BOOL)parseContentByPID:(NSData *)arg_data
{
    if(nil==arg_data||[arg_data length]<=0)
    {
        NSLog(@"Data pass into parseContentByPID is empty");
        return NO;
    }
    switch (recvPID_) {
        case PIDRtt:
        {
            //RoundTripTime
                        
            RoundTripTime *_roundTT=[RoundTripTime parseFromData:recvBuf_];
            
            if(nil==_roundTT)
            {
                NSLog(@"Parse RoundTripTime msg failed");
                [self resetInvali];
                [self disConnect];
                return NO;
            }
            
            
            if(![_roundTT isProof])
            {
                NSLog(@"RTT Trans-test Ended");
                
                if([_roundTT rtt]<=0||[_roundTT deltaTime]<=0)
                {
                    //value getting error
                    NSLog(@"get rtt or deltaTime of Received error");
                    
                    [self resetInvali];
                    [self disConnect];
                    return NO;
                }
                
                //save delta Time stuff into local;
                if(nil==userDefaults_)
                {
                    userDefaults_=[NSUserDefaults standardUserDefaults];
                }
                
                [userDefaults_ setValue: 
                 [NSString stringWithFormat:@"%d",[_roundTT rtt]]
                                 forKey:@"RTT"];
                [userDefaults_ setValue: 
                 [NSString stringWithFormat:@"%d",[_roundTT deltaTime]]
                                 forKey:@"DeltaTime"];
                
                [userDefaults_ synchronize];
                
                return YES;
            }
                
            //rtt testing
            if([_roundTT srvTime]<=0)
            {
                    NSLog(@"get srvTime of RoundTripTime failed");
                    [self resetInvali];
                    [self disConnect];
                    
                    return NO;
            }
                
                
            //set recvSrvTime to clnTime
            int64_t _curClnTime=[AppDelegate currentDateTime];
        
            _roundTT=[[[_roundTT builder]setClnTime:_curClnTime]build];
                
            NSData *_roundTTData=[_roundTT data];
                
            //send back roundTTData to server; 
            [self sendData:_roundTTData Pid:PIDRtt]; 
            break;
        
        }
            
        case PIDAck:
        {
            //Ack:
            //login or regist msg;
            
            NSLog(@"into PID ACK handle");
            Ack *_ack=[Ack parseFromData:recvBuf_];
            if (nil==_ack) {
                NSLog(@"Parse Ack failed");
                
                [self resetInvali];
                [self disConnect];
                
                return NO;
            }
            PID _ackWhat=[_ack what];
            if(_ackWhat==PIDUser)
            {
                //user info
                if ([_ack action] == ActionCreate
                    ||[_ack action]==ActionLogin
                    ||[_ack action]==ActionLogout) {
                    [self->callbackDelegate loginCallback:_ack];

                }
            }
            break;
        }
        case PIDMsg:
        case PIDMove:
        {
            NSLog(@"move call");
            Move *_move = [Move parseFromData:recvBuf_];
            [self->callbackDelegate moveCallback:_move];
        }
        case PIDUser:
            break;
        default:
        {
            NSLog(@"recvPID_ is not legal one");
            
            [self resetInvali];
            
            return NO;
        }
            break;
    }
    
    [self resetInvali];
    //again to read header
    
    
    return YES;
    
}

//reset all variable using for TransData Write/Read
-(void)resetInvali
{
    shouldRcvBytes_=0;
    recvBytes_=0;
    continueRcvBytes_=0;
    recvPID_=0;
    [recvBuf_ setData:nil];
}


@end
